package com.suma.platform.core.service.impl;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.nfsq.platform.utils.encryption.MD5Utils;
import com.suma.platform.core.dto.Exposer;
import com.suma.platform.core.dto.SeckillExecution;
import com.suma.platform.core.enums.SeckillStatEnum;
import com.suma.platform.core.exception.RepeatKillException;
import com.suma.platform.core.exception.SeckillCloseException;
import com.suma.platform.core.exception.SeckillException;
import com.suma.platform.core.mapper.SeckillMapper;
import com.suma.platform.core.mapper.SeckillRecordMapper;
import com.suma.platform.core.model.Seckill;
import com.suma.platform.core.model.SeckillRecord;
import com.suma.platform.core.service.SeckillService;

import lombok.extern.slf4j.Slf4j;

/**
 * @author suma
 * @date 2018/2/12 上午10:14
 */
@Slf4j
@Service
public class SeckillServiceImpl implements SeckillService {

	// 加入一个混淆字符串(秒杀接口)的salt，为了我避免用户猜出我们的md5值，值任意给，越复杂越好
	private static final String SALT = "pokmidjnfsuma123";

	@Autowired
	SeckillMapper seckillMapper;

	@Autowired
	SeckillRecordMapper seckillRecordMapper;

	@Override
	public List<Seckill> getSeckillList() {
		return seckillMapper.selectByExample(null);
	}

	@Override
	public Seckill getById(long seckillId) {
		return seckillMapper.selectByPrimaryKey(seckillId);
	}

	@Cacheable(cacheNames = "seckillUrl",key = "#seckillId")
	@Override
	public Exposer exportSeckillUrl(long seckillId) {
		Seckill seckill = getById(seckillId);
		Date startTime = seckill.getStartTime();
		Date endTime = seckill.getEndTime();
		Date nowTime = new Date();
		// 若是秒杀未开启
		if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) {
			return new Exposer(Boolean.FALSE, seckillId, nowTime.getTime(), startTime.getTime(), endTime.getTime());
		}

		// 秒杀开启，返回秒杀商品的id、用给接口加密的md5
		String md5 = MD5Utils.getMD5(seckillId + SALT);
		return new Exposer(Boolean.TRUE, md5, seckillId);
	}

	@Override
	public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException {
		if (md5 == null || !md5.equals(MD5Utils.getMD5(seckillId + SALT))) {
			throw new SeckillException("seckill data rewrite");
		}
		// 执行秒杀逻辑：减库存+记录购买行为
		Date nowTime = new Date();
		try {
			SeckillRecord seckillRecord = new SeckillRecord();
			seckillRecord.setSeckillId(seckillId);
			seckillRecord.setUserPhone(userPhone);

			// 否则更新了库存，秒杀成功,增加明细
			int insertCount = seckillRecordMapper.insertSelective(seckillRecord);
			// 看是否该明细被重复插入，即用户是否重复秒杀
			if (insertCount <= 0) {
				throw new RepeatKillException("seckill repeated");
			} else {
				// 减库存,热点商品竞争，update方法会拿到行级锁
				int updateCount = seckillMapper.reduceNumber(seckillId, nowTime);
				if (updateCount <= 0) {
					// 没有更新库存记录，说明秒杀结束 rollback
					throw new SeckillCloseException("seckill is closed");
				} else {
					// 秒杀成功,得到成功插入的明细记录,并返回成功秒杀的信息 commit
					return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, seckillRecord);
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			// 所有编译器异常，转化成运行期异常
			throw new SeckillException("seckill inner error:" + e.getMessage());
		}
	}

	@Override
	public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) throws SeckillException {
		return null;
	}
}
